﻿using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Centido.Core.Test
{
    /// <summary>
    ///   Restoring the schema.
    /// </summary>
    [TestClass]
    public class RestoringSchemaTest
    {
        /// <summary>
        ///   Domain name
        /// </summary>
        public const string DomainName = "FullAdminRights";

        /// <summary>
        ///   Domain
        /// </summary>
        private Domain _domain;


        /// <summary>
        ///   Initializing the test
        /// </summary>
        [TestInitialize]
        public void Initialize()
        {
            _domain = Domain.Build(DomainConfiguration.Load(DomainName));

            _domain.DeleteAll();
        }


        /// <summary>
        ///   Cleaning up after the test
        /// </summary>
        [TestCleanup]
        public void CleanUp()
        {
            _domain.DeleteAll();
        }


        /// <summary>
        ///   Backuping and restoring the current domain.
        /// </summary>
        private void BackupAndRestore()
        {
            // Creating a backup
            new BackupTask(_domain).Run();

            // Deleting all tables
            _domain.DeleteAllTables();

            Assert.AreEqual(_domain.Tables.Count, 0);

            // Restoring
            new RestoreTask(_domain).Run();
        }


        /// <summary>
        ///   Restoring a table list.
        /// </summary>
        [TestMethod]
        public void RestoringTableList()
        {
            // Creating two tables
            var firstTableName = StringProperty.RandomIdentifier();
            _domain.AddTable(firstTableName);

            var secondTableName = StringProperty.RandomIdentifier();
            _domain.AddTable(secondTableName);

            Assert.AreEqual(_domain.Tables.Count, 2);

            BackupAndRestore();

            Assert.AreEqual(_domain.Tables.Count, 2);
            Assert.AreEqual(_domain.Tables.Count(t => t.Name == firstTableName.ToLower()), 1);
            Assert.AreEqual(_domain.Tables.Count(t => t.Name == secondTableName.ToLower()), 1);
        }


        /// <summary>
        ///   Restoring table properties.
        /// </summary>
        [TestMethod]
        public void RestoringTableProperties()
        {
            // Creating a table
            var tableName = StringProperty.RandomIdentifier();
            var table = _domain.AddTable(tableName);

            // Setting table properties
            table.Description = "The table description";
            table.DisplayName = "The table display name";
            table.HasUnitLevelSecurity = false;

            BackupAndRestore();

            var restoredTable = _domain.GetTable(tableName);

            Assert.IsNotNull(restoredTable);
            Assert.AreEqual(restoredTable.DisplayName, "The table display name");
            Assert.AreEqual(restoredTable.Description, "The table description");
            Assert.IsFalse(restoredTable.HasUnitLevelSecurity);
        }


        /// <summary>
        ///   Restoring table columns.
        /// </summary>
        [TestMethod]
        public void RestoringTableColumns()
        {
            // Creating a table
            var table = _domain.AddTable("my_table");

            // Creating table columns of all types
            table.AddColumn("big_int_col", ColumnType.BigInteger);
            table.AddColumn("bin_col", ColumnType.Binary);
            table.AddColumn("bool_col", ColumnType.Boolean);
            table.AddColumn("date_col", ColumnType.DateTime);
            table.AddColumn("dec_col", ColumnType.Decimal);
            table.AddColumn("enum_col", ColumnType.Enumeration);
            table.AddColumn("guid_col", ColumnType.Guid);
            table.AddColumn("int_col", ColumnType.Integer);
            table.AddColumn("ref_col", ColumnType.Reference);
            table.AddColumn("small_int_col", ColumnType.SmallInteger);
            table.AddColumn("str_col", ColumnType.String);
            table.AddColumn("text_col", ColumnType.Text);

            BackupAndRestore();

            var restoredTable = _domain.GetTable("my_table");

            Assert.IsNotNull(restoredTable.GetColumn("big_int_col"));
            Assert.AreEqual(restoredTable.GetColumn("big_int_col").ColumnType, 
                ColumnType.BigInteger);

            Assert.IsNotNull(restoredTable.GetColumn("bin_col"));
            Assert.AreEqual(restoredTable.GetColumn("bin_col").ColumnType,
                ColumnType.Binary);

            Assert.IsNotNull(restoredTable.GetColumn("bool_col"));
            Assert.AreEqual(restoredTable.GetColumn("bool_col").ColumnType,
                ColumnType.Boolean);

            Assert.IsNotNull(restoredTable.GetColumn("date_col"));
            Assert.AreEqual(restoredTable.GetColumn("date_col").ColumnType,
                ColumnType.DateTime);

            Assert.IsNotNull(restoredTable.GetColumn("dec_col"));
            Assert.AreEqual(restoredTable.GetColumn("dec_col").ColumnType,
                ColumnType.Decimal);

            Assert.IsNotNull(restoredTable.GetColumn("enum_col"));
            Assert.AreEqual(restoredTable.GetColumn("enum_col").ColumnType,
                ColumnType.Enumeration);

            Assert.IsNotNull(restoredTable.GetColumn("guid_col"));
            Assert.AreEqual(restoredTable.GetColumn("guid_col").ColumnType,
                ColumnType.Guid);

            Assert.IsNotNull(restoredTable.GetColumn("int_col"));
            Assert.AreEqual(restoredTable.GetColumn("int_col").ColumnType,
                ColumnType.Integer);

            Assert.IsNotNull(restoredTable.GetColumn("ref_col"));
            Assert.AreEqual(restoredTable.GetColumn("ref_col").ColumnType,
                ColumnType.Reference);

            Assert.IsNotNull(restoredTable.GetColumn("small_int_col"));
            Assert.AreEqual(restoredTable.GetColumn("small_int_col").ColumnType,
                ColumnType.SmallInteger);

            Assert.IsNotNull(restoredTable.GetColumn("str_col"));
            Assert.AreEqual(restoredTable.GetColumn("str_col").ColumnType,
                ColumnType.String);

            Assert.IsNotNull(restoredTable.GetColumn("text_col"));
            Assert.AreEqual(restoredTable.GetColumn("text_col").ColumnType,
                ColumnType.Text);
        }


        /// <summary>
        ///   Restoring big integer column properties.
        /// </summary>
        [TestMethod]
        public void RestoringBigIntColumnProperties()
        {
            // Creating a big integer column
            var table = _domain.AddTable("my_table");

            var columnName = StringProperty.RandomIdentifier();
            var column = BigIntColumn.Ensure(table, columnName);

            // Setting the column properties
            column.DisplayName = "The column display name";
            column.Description = "The column description";
            column.DefaultValue = -67;
            column.IsNotNull = true;
            column.Maximum = 124;
            column.Minimum = -89;
            column.OrderIndex = 56;

            BackupAndRestore();

            var restoredTable = _domain.GetTable("my_table");

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName)
                as BigIntColumn;

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.DisplayName, "The column display name");
            Assert.AreEqual(restoredColumn.Description, "The column description");
            Assert.AreEqual(restoredColumn.DefaultValue, -67);
            Assert.AreEqual(restoredColumn.IsNotNull, true);
            Assert.AreEqual(restoredColumn.Maximum, 124);
            Assert.AreEqual(restoredColumn.Minimum, -89);
            Assert.AreEqual(restoredColumn.OrderIndex, 56);
        }


        /// <summary>
        ///   Restoring binary column properties.
        /// </summary>
        [TestMethod]
        public void RestoringBinaryColumnProperties()
        {
            // Creating a binary column
            var table = _domain.AddTable("my_table");

            var columnName = StringProperty.RandomIdentifier();
            var column = BinaryColumn.Ensure(table, columnName);

            // Setting the column properties
            column.DisplayName = "The column display name";
            column.Description = "The column description";
            column.IsNotNull = true;
            column.OrderIndex = 56;

            BackupAndRestore();

            var restoredTable = _domain.GetTable("my_table");

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName)
                as BinaryColumn;

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.DisplayName, "The column display name");
            Assert.AreEqual(restoredColumn.Description, "The column description");
            Assert.AreEqual(restoredColumn.IsNotNull, true);
            Assert.AreEqual(restoredColumn.OrderIndex, 56);
        }


        /// <summary>
        ///   Restoring bool column properties.
        /// </summary>
        [TestMethod]
        public void RestoringBoolColumnProperties()
        {
            // Creating a bool column
            var table = _domain.AddTable("my_table");

            var columnName = StringProperty.RandomIdentifier();
            var column = BoolColumn.Ensure(table, columnName);

            // Setting the column properties
            column.DisplayName = "The column display name";
            column.Description = "The column description";
            column.DefaultValue = true;
            column.IsNotNull = true;
            column.OrderIndex = 56;

            BackupAndRestore();

            var restoredTable = _domain.GetTable("my_table");

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName)
                as BoolColumn;

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.DisplayName, "The column display name");
            Assert.AreEqual(restoredColumn.Description, "The column description");
            Assert.AreEqual(restoredColumn.DefaultValue, true);
            Assert.AreEqual(restoredColumn.IsNotNull, true);
            Assert.AreEqual(restoredColumn.OrderIndex, 56);
        }


        /// <summary>
        ///   Restoring datetime column properties.
        /// </summary>
        [TestMethod]
        public void RestoringDateTimeColumnProperties()
        {
            // Creating a datetime column
            var table = _domain.AddTable("my_table");

            var columnName = StringProperty.RandomIdentifier();
            var column = DateTimeColumn.Ensure(table, columnName);

            // Setting the column properties
            column.DisplayName = "The column display name";
            column.Description = "The column description";
            column.DefaultValue = new DateTime(2011, 1, 1, 23, 12, 56);
            column.IsNotNull = true;
            column.Maximum = new DateTime(2012, 1, 3, 13, 12, 56);
            column.Minimum = new DateTime(2010, 1, 3, 13, 16, 56);
            column.OrderIndex = 56;

            BackupAndRestore();

            var restoredTable = _domain.GetTable("my_table");

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName)
                as DateTimeColumn;

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.DisplayName, "The column display name");
            Assert.AreEqual(restoredColumn.Description, "The column description");
            Assert.AreEqual(restoredColumn.DefaultValue, new DateTime(2011, 1, 1, 23, 12, 56));
            Assert.AreEqual(restoredColumn.IsNotNull, true);
            Assert.AreEqual(restoredColumn.Maximum, new DateTime(2012, 1, 3, 13, 12, 56));
            Assert.AreEqual(restoredColumn.Minimum, new DateTime(2010, 1, 3, 13, 16, 56));
            Assert.AreEqual(restoredColumn.OrderIndex, 56);
        }


        /// <summary>
        ///   Restoring decimal column properties.
        /// </summary>
        [TestMethod]
        public void RestoringDecimalColumnProperties()
        {
            // Creating a decimal column
            var table = _domain.AddTable("my_table");

            var columnName = StringProperty.RandomIdentifier();
            var column = DecimalColumn.Ensure(table, columnName);

            // Setting the column properties
            column.DisplayName = "The column display name";
            column.Description = "The column description";
            column.DefaultValue = -67.5M;
            column.IsNotNull = true;
            column.Maximum = 124.3M;
            column.Minimum = -89.2M;
            column.OrderIndex = 56;
            column.Precision = 15;
            column.Scale = 4;

            BackupAndRestore();

            var restoredTable = _domain.GetTable("my_table");

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName)
                as DecimalColumn;

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.DisplayName, "The column display name");
            Assert.AreEqual(restoredColumn.Description, "The column description");
            Assert.AreEqual(restoredColumn.DefaultValue, -67.5M);
            Assert.AreEqual(restoredColumn.IsNotNull, true);
            Assert.AreEqual(restoredColumn.Maximum, 124.3M);
            Assert.AreEqual(restoredColumn.Minimum, -89.2M);
            Assert.AreEqual(restoredColumn.OrderIndex, 56);
            Assert.AreEqual(restoredColumn.Precision, 15);
            Assert.AreEqual(restoredColumn.Scale, 4);
        }


        /// <summary>
        ///   Restoring guid column properties.
        /// </summary>
        [TestMethod]
        public void RestoringGuidColumnProperties()
        {
            // Creating a guid column
            var table = _domain.AddTable("my_table");

            var columnName = StringProperty.RandomIdentifier();
            var column = GuidColumn.Ensure(table, columnName);

            var defaultValue = Guid.NewGuid();

            // Setting the column properties
            column.DisplayName = "The column display name";
            column.Description = "The column description";
            column.DefaultValue = defaultValue;
            column.IsNotNull = true;
            column.OrderIndex = 56;

            BackupAndRestore();

            var restoredTable = _domain.GetTable("my_table");

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName)
                as GuidColumn;

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.DisplayName, "The column display name");
            Assert.AreEqual(restoredColumn.Description, "The column description");
            Assert.AreEqual(restoredColumn.DefaultValue, defaultValue);
            Assert.AreEqual(restoredColumn.IsNotNull, true);
            Assert.AreEqual(restoredColumn.OrderIndex, 56);
        }


        /// <summary>
        ///   Restoring integer column properties.
        /// </summary>
        [TestMethod]
        public void RestoringIntColumnProperties()
        {
            // Creating a integer column
            var table = _domain.AddTable("my_table");

            var columnName = StringProperty.RandomIdentifier();
            var column = IntColumn.Ensure(table, columnName);

            // Setting the column properties
            column.DisplayName = "The column display name";
            column.Description = "The column description";
            column.DefaultValue = -67;
            column.IsNotNull = true;
            column.Maximum = 124;
            column.Minimum = -89;
            column.OrderIndex = 56;

            BackupAndRestore();

            var restoredTable = _domain.GetTable("my_table");

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName)
                as IntColumn;

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.DisplayName, "The column display name");
            Assert.AreEqual(restoredColumn.Description, "The column description");
            Assert.AreEqual(restoredColumn.DefaultValue, -67);
            Assert.AreEqual(restoredColumn.IsNotNull, true);
            Assert.AreEqual(restoredColumn.Maximum, 124);
            Assert.AreEqual(restoredColumn.Minimum, -89);
            Assert.AreEqual(restoredColumn.OrderIndex, 56);
        }


        /// <summary>
        ///   Restoring small integer column properties.
        /// </summary>
        [TestMethod]
        public void RestoringSmallIntColumnProperties()
        {
            // Creating a small integer column
            var table = _domain.AddTable("my_table");

            var columnName = StringProperty.RandomIdentifier();
            var column = SmallIntColumn.Ensure(table, columnName);

            // Setting the column properties
            column.DisplayName = "The column display name";
            column.Description = "The column description";
            column.DefaultValue = -67;
            column.IsNotNull = true;
            column.Maximum = 124;
            column.Minimum = -89;
            column.OrderIndex = 56;

            BackupAndRestore();

            var restoredTable = _domain.GetTable("my_table");

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName)
                as SmallIntColumn;

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.DisplayName, "The column display name");
            Assert.AreEqual(restoredColumn.Description, "The column description");
            Assert.AreEqual(restoredColumn.DefaultValue, (short)-67);
            Assert.AreEqual(restoredColumn.IsNotNull, true);
            Assert.AreEqual(restoredColumn.Maximum, (short)124);
            Assert.AreEqual(restoredColumn.Minimum, (short)-89);
            Assert.AreEqual(restoredColumn.OrderIndex, 56);
        }


        /// <summary>
        ///   Restoring string column properties.
        /// </summary>
        [TestMethod]
        public void RestoringStringColumnProperties()
        {
            // Creating a string column
            var table = _domain.AddTable("my_table");

            var columnName = StringProperty.RandomIdentifier();
            var column = StringColumn.Ensure(table, columnName);

            // Setting the column properties
            column.DisplayName = "The column display name";
            column.Description = "The column description";
            column.DefaultValue = "default value";
            column.IsNotNull = true;
            column.OrderIndex = 56;
            column.MaxLength = 135;

            BackupAndRestore();

            var restoredTable = _domain.GetTable("my_table");

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName)
                as StringColumn;

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.DisplayName, "The column display name");
            Assert.AreEqual(restoredColumn.Description, "The column description");
            Assert.AreEqual(restoredColumn.DefaultValue, "default value");
            Assert.AreEqual(restoredColumn.IsNotNull, true);
            Assert.AreEqual(restoredColumn.OrderIndex, 56);
            Assert.AreEqual(restoredColumn.MaxLength, 135);
        }


        /// <summary>
        ///   Restoring text column properties.
        /// </summary>
        [TestMethod]
        public void RestoringTextColumnProperties()
        {
            // Creating a text column
            var table = _domain.AddTable("my_table");

            var columnName = StringProperty.RandomIdentifier();
            var column = TextColumn.Ensure(table, columnName);

            // Setting the column properties
            column.DisplayName = "The column display name";
            column.Description = "The column description";
            column.IsNotNull = true;
            column.OrderIndex = 56;

            BackupAndRestore();

            var restoredTable = _domain.GetTable("my_table");

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName)
                as TextColumn;

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.DisplayName, "The column display name");
            Assert.AreEqual(restoredColumn.Description, "The column description");
            Assert.AreEqual(restoredColumn.IsNotNull, true);
            Assert.AreEqual(restoredColumn.OrderIndex, 56);
        }


        /// <summary>
        ///   Restoring enumeration column properties.
        /// </summary>
        [TestMethod]
        public void RestoringEnumColumnProperties()
        {
            // Creating a enumeration column
            var table = _domain.AddTable("my_table");

            var columnName = StringProperty.RandomIdentifier();
            var column = EnumColumn.Ensure(table, columnName);

            // Creating enum members
            column.AddMember(1).DisplayName = "The first member";
            column.AddMember(2).DisplayName = "The second member";

            // Setting the column properties
            column.DisplayName = "The column display name";
            column.Description = "The column description";
            column.DefaultValue = 1;
            column.IsNotNull = true;
            column.OrderIndex = 56;
            column.HasValueChecking = false;

            BackupAndRestore();

            var restoredTable = _domain.GetTable("my_table");

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName)
                as EnumColumn;

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.DisplayName, "The column display name");
            Assert.AreEqual(restoredColumn.Description, "The column description");
            Assert.AreEqual(restoredColumn.DefaultValue, 1);
            Assert.AreEqual(restoredColumn.IsNotNull, true);
            Assert.AreEqual(restoredColumn.OrderIndex, 56);
            Assert.AreEqual(restoredColumn.HasValueChecking, false);

            Assert.AreEqual(restoredColumn.Members.Count, 2);
            Assert.AreEqual(restoredColumn.Members
                .Count(m => m.Value == 1 && m.DisplayName == "The first member"), 1);
            Assert.AreEqual(restoredColumn.Members
                .Count(m => m.Value == 2 && m.DisplayName == "The second member"), 1);
        }


        /// <summary>
        ///   Restoring reference column properties.
        /// </summary>
        [TestMethod]
        public void RestoringRefColumnProperties()
        {
            // Creating a reference column
            var table = _domain.AddTable("my_table");
            var anotherTable = _domain.AddTable("another_table");
            var yetAnotherTable = _domain.AddTable("yet_another_table");

            var columnName = StringProperty.RandomIdentifier();
            var column = RefColumn.Ensure(table, columnName);

            // Creating reference targets
            column.AddTarget(anotherTable).HasCascadeDelete = true;
            column.AddTarget(yetAnotherTable).HasCascadeDelete = false;

            var defaultValue = Guid.NewGuid();

            // Setting the column properties
            column.DisplayName = "The column display name";
            column.Description = "The column description";
            column.DefaultValue = defaultValue;
            column.IsNotNull = true;
            column.OrderIndex = 56;
            column.HasReferenceChecking = true;

            BackupAndRestore();

            var restoredTable = _domain.GetTable("my_table");

            Assert.IsNotNull(restoredTable);

            var restoredColumn = restoredTable.GetColumn(columnName)
                as RefColumn;

            Assert.IsNotNull(restoredColumn);

            Assert.AreEqual(restoredColumn.DisplayName, "The column display name");
            Assert.AreEqual(restoredColumn.Description, "The column description");
            Assert.AreEqual(restoredColumn.DefaultValue, defaultValue);
            Assert.AreEqual(restoredColumn.IsNotNull, true);
            Assert.AreEqual(restoredColumn.OrderIndex, 56);
            Assert.AreEqual(restoredColumn.HasReferenceChecking, true);

            Assert.AreEqual(restoredColumn.Targets.Count, 2);
            Assert.AreEqual(restoredColumn.Targets
                .Count(t => t.RefTable.Name == "another_table" && t.HasCascadeDelete), 1);
            Assert.AreEqual(restoredColumn.Targets
                .Count(t => t.RefTable.Name == "yet_another_table" && !t.HasCascadeDelete), 1);
        }


        /// <summary>
        ///   Restoring table indexes.
        /// </summary>
        [TestMethod]
        public void RestoringTableIndexes()
        {
            // Creating a table
            var table = _domain.AddTable("my_table");

            var systemIndexCount = table.Indexes.Count;

            // Creating two columns
            var col1 = table.AddColumn("col_1", ColumnType.Integer);
            var col2 = table.AddColumn("col_2", ColumnType.Boolean);

            // Creating two indexes
            table.AddIndex(table.SystemColumns.DeletedOn, col1);
            table.AddIndex(true, col1, col2);

            BackupAndRestore();

            var restoredTable = _domain.GetTable("my_table");

            Assert.IsNotNull(restoredTable);

            Assert.AreEqual(restoredTable.Indexes.Count, systemIndexCount + 2);

            Assert.AreEqual(restoredTable.Indexes.Count(i => i.IsUnique
                && i.Columns.Any(c => c.Name == col1.Name)
                && i.Columns.Any(c => c.Name == col2.Name)), 1);
            Assert.AreEqual(restoredTable.Indexes.Count(i => !i.IsUnique
                && i.Columns.Any(c => c.Name == col1.Name)
                && i.Columns.Any(c => c.Name == restoredTable.SystemColumns.DeletedOn.Name)), 1);
        }


        /// <summary>
        ///   Restoring domain options.
        /// </summary>
        [TestMethod]
        public void DomainOptions()
        {
            _domain.HasRecycleBin = false;

            BackupAndRestore();

            Assert.IsFalse(_domain.HasRecycleBin);

            _domain.HasRecycleBin = true;

            BackupAndRestore();

            Assert.IsTrue(_domain.HasRecycleBin);
        }
    }
}
